package redneuronal;
/**
 * Neural Network
 * Feedforward Backpropagation Neural Network
 * Written in 2002 by Jeff Heaton(http://www.jeffheaton.com)
 *
 * This class is released under the limited GNU public
 * license (LGPL).
 *
 * @author Jeff Heaton
 * @version 1.0
 */

public class Network {
    // Error global 
    protected double globalError;
    // Número de neuronas de la capa entrada
    protected int inputCount;
    // Número de neuronas de la capa oculta
    protected int hiddenCount;
    // Número de neuronas de la capa salida
    protected int outputCount;
    // Número total de neuronas en la red
    protected int neuronCount;
    // Número de pesos en la red
    protected int weightCount;
    // La tasa de aprredizaje
    protected double learnRate;
    //Salidas de los distintos niveles
    protected double fire[];
    /**
    *  El peso matriciales Esta, junto con los umbrales representa la mejoria
    *  de la red neuronal. 
    */
     protected double matrix[];
     // Arreglo con los errores del último cálculo
     protected double error[];
     //Matriz delta de entrenamiento
    protected double accMatrixDelta[];
    /* Los umbrales, este valor, junto con la matriz de pesos 
    * Puede ser pensado como la memoria de la red neuronal. */
    protected double thresholds[];
    // Los cambios que se deben aplicar al peso
    protected double matrixDelta[];
    //La acumulación de los deltas de umbral.
    protected double accThresholdDelta[];
    //Los delta del umbral
    protected double thresholdDelta[];
    // El momento para el entrenamiento
    protected double momentum;
    //Cambios en lso errores
    protected double errorDelta[];
 /**
  * Constructur de la red neuronal
  *
  * @param inputCount Número de neuronas de entrada
  * @param hiddenCount Número de neuronas de la capa oculta
  * @param outputCount Número de neuronas de salida
  * @param learnRate Tasa de aprendizaje utilizada en el entrenamiento
  * @param momentum El momento deel entrenamiento
  */
    public Network(int inputCount, int hiddenCount, int outputCount, double learnRate, double momentum) {
        // Asigna los valores a las variables globales
        this.learnRate = learnRate;
        this.momentum = momentum;
        this.inputCount = inputCount;
        this.hiddenCount = hiddenCount;
        this.outputCount = outputCount;
        // Cuenta la cantidad de neuronas
        neuronCount = inputCount + hiddenCount + outputCount;
        weightCount = (inputCount * hiddenCount) + (hiddenCount * outputCount);
        // Contruye las matrices
        fire    = new double[neuronCount];
        matrix   = new double[weightCount];
        matrixDelta = new double[weightCount];
        thresholds = new double[neuronCount];
        errorDelta = new double[neuronCount];
        error    = new double[neuronCount];
        accThresholdDelta = new double[neuronCount];
        accMatrixDelta = new double[weightCount];
        thresholdDelta = new double[neuronCount];
        reset();
    }
    /**
    * Devuelve el error cuadrático medio de un conjunto de entrenamiento completo.
    *
    * @param len La longitud de un conjunto de entrenamiento completo.
    * @return Retorna El error actual para la red neuronal.
    */
    public double getError(int len) {
        double err = Math.sqrt(globalError / (len * outputCount));
        globalError = 0; // limpia la variable global
        return err;
    }
    /**
    * El método del umbral. Es posible que desee para anular esta clase para proporcionar otra 
    * métodos de umbral.
    * @param sum La activación de la neurona
    * @return Retorna la activación aplicada al método de umbral.
    */
    public double threshold(double sum) {
        return 1.0 / (1 + Math.exp(-1.0 * sum));
    }

    /**
    * Calcular la salida para una entrada dada a la red neural.
    *
    * @param Entrada La entrada proporciona a la red neural.
    * @return Los resultados de las neuronas de salida.
    */
    public double[] computeOutputs(double input[]) {
        int i, j;
        final int hiddenIndex = inputCount;
        final int outIndex = inputCount + hiddenCount;
        for (i = 0; i < inputCount; i++) {
            fire[i] = input[i];
        }
        // Primera capa
        int inx = 0;
        for (i = hiddenIndex; i < outIndex; i++) {
            double sum = thresholds[i];
            for (j = 0; j < inputCount; j++) {
                sum += fire[j] * matrix[inx++];
            }
            fire[i] = threshold(sum);
        }
        // Capa oculta
        double result[] = new double[outputCount];
        for (i = outIndex; i < neuronCount; i++) {
            double sum = thresholds[i];
            for (j = hiddenIndex; j < outIndex; j++) {
                sum += fire[j] * matrix[inx++];
            }
            fire[i] = threshold(sum);
            result[i-outIndex] = fire[i];
        }
        return result;
    }

    /**
    * Calcular el error para el recogntion acaba de hacer.
    * @param Qué ideales las neuronas de salida debe haber arrojado.
    */
    public void calcError(double ideal[]) {
        int i, j;
        final int hiddenIndex = inputCount;
        final int outputIndex = inputCount + hiddenCount;
        // clear hidden layer errors
        for (i = inputCount; i < neuronCount; i++) {
            error[i] = 0;
        }
        // errores de capa y los deltas de capa de salida
        for (i = outputIndex; i < neuronCount; i++) {
            error[i] = ideal[i - outputIndex] - fire[i];
            globalError += error[i] * error[i];
            errorDelta[i] = error[i] * fire[i] * (1 - fire[i]);
        }
        // errores capa oculta
        int winx = inputCount * hiddenCount;
        for (i = outputIndex; i < neuronCount; i++) {
            for (j = hiddenIndex; j < outputIndex; j++) {
                accMatrixDelta[winx] += errorDelta[i] * fire[j];
                error[j] += matrix[winx] * errorDelta[i];
                winx++;
            }
            accThresholdDelta[i] += errorDelta[i];
        }
        // deltas capa oculta
        for (i = hiddenIndex; i < outputIndex; i++) {
            errorDelta[i] = error[i] * fire[i] * (1 - fire[i]);
        }
        // errores capa de entrada
        winx = 0; // desplazamiento en serie de peso
        for (i = hiddenIndex; i < outputIndex; i++) {
            for (j = 0; j < hiddenIndex; j++) {
                accMatrixDelta[winx] += errorDelta[i] * fire[j];
                error[j] += matrix[winx] * errorDelta[i];
                winx++;
            }
            accThresholdDelta[i] += errorDelta[i];
        }
    }
    /**
    * Modificar la matriz de ponderación y umbrales basados ​​en la última llamada
    * calcError.
    */
    public void learn() {
        int i;
        // procesar la matriz
        for (i = 0; i < matrix.length; i++) {
            matrixDelta[i] = (learnRate * accMatrixDelta[i]) + (momentum * matrixDelta[i]);
            matrix[i] += matrixDelta[i];
            accMatrixDelta[i] = 0;
        }
        // procesar los umbrales
        for (i = inputCount; i < neuronCount; i++) {
            thresholdDelta[i] = learnRate * accThresholdDelta[i] + (momentum * thresholdDelta[i]);
            thresholds[i] += thresholdDelta[i];
            accThresholdDelta[i] = 0;
        }
    }
    /**
    * Reiniciar la matriz de pesos y los umbrales.
    */
    public void reset() {
        int i;
        for (i = 0; i < neuronCount; i++) {
            thresholds[i] = 0.5 - (Math.random());
            thresholdDelta[i] = 0;
            accThresholdDelta[i] = 0;
        }
        for (i = 0; i < matrix.length; i++) {
            matrix[i] = 0.5 - (Math.random());
            matrixDelta[i] = 0;
            accMatrixDelta[i] = 0;
        }
    }
}